Skip to content

feat: support reverse and bidirectional arrows in DSL#30

Open
swiftlysingh wants to merge 1 commit intomainfrom
feat/issue-18-bidir-arrows
Open

feat: support reverse and bidirectional arrows in DSL#30
swiftlysingh wants to merge 1 commit intomainfrom
feat/issue-18-bidir-arrows

Conversation

@swiftlysingh
Copy link
Copy Markdown
Owner

Summary

  • add DSL parser support for <- and <->
  • preserve logical graph direction for reverse arrows while rendering correct Excalidraw arrowheads
  • cover parser, generator, and CLI output, and document the new syntax

Testing

  • npm run build
  • npm run lint
  • npm run test:run
  • node dist/cli.js create --inline "[A] <- [B]
    [C] <-> [D]" -o tests/tmp/manual/arrow-directions.excalidraw

Copilot AI review requested due to automatic review settings April 10, 2026 21:31
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 10, 2026

Warning

Rate limit exceeded

@swiftlysingh has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 27 minutes and 43 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 27 minutes and 43 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4f02d389-07f2-4140-b5ee-5e02eeb501c5

📥 Commits

Reviewing files that changed from the base of the PR and between a26f7dd and c8078a0.

📒 Files selected for processing (5)
  • README.md
  • src/factory/connection-factory.ts
  • src/parser/dsl-parser.ts
  • tests/integration/exporter/cli-export.test.ts
  • tests/unit/parser/dsl-parser.test.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-18-bidir-arrows

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support in the flowchart DSL for reverse (<-) and bidirectional (<->) connections, aiming to keep graph semantics consistent while emitting the correct Excalidraw arrowhead configuration through the generator/CLI.

Changes:

  • Extend DSL tokenization/parsing to recognize <- and <-> and attach arrowhead style metadata.
  • Adjust Excalidraw arrow creation to preserve explicit null arrowhead values (instead of defaulting via ??).
  • Add unit/integration tests and document the new DSL syntax in the README.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/unit/parser/dsl-parser.test.ts Adds coverage for reverse/bidirectional parsing and arrowhead output (currently has ordering brittleness and expectation mismatch).
tests/integration/exporter/cli-export.test.ts Validates CLI create --inline preserves arrowhead configuration (currently order-dependent).
src/parser/dsl-parser.ts Implements <- / <-> tokenization and edge creation with pending style flags (contains a reverse-arrow semantic/render mismatch).
src/factory/connection-factory.ts Fixes arrowhead defaulting so explicit null is preserved (enables start/end arrowhead control).
README.md Documents new arrow syntaxes and provides examples.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/parser/dsl-parser.ts
Comment on lines +617 to +621
...(pendingBidirectional
? { startArrowhead: 'arrow' as const, endArrowhead: 'arrow' as const }
: pendingReversed
? { startArrowhead: 'arrow' as const, endArrowhead: null }
: {}),
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For <- you’re currently (a) swapping source/target (so the edge is already B→A) and (b) also forcing startArrowhead: 'arrow' + endArrowhead: null. Those two together invert the rendered arrow direction relative to the logical edge. If the edge direction is reversed via source/target swap, the arrowheads should typically remain the default (start null, end 'arrow') or explicitly set startArrowhead: null, endArrowhead: 'arrow'—but not start-only.

Copilot uses AI. Check for mistakes.
Comment on lines +105 to +109
expect(result.edges[0].target).toBe(result.nodes[0].id);
expect(result.edges[0].style).toEqual({
startArrowhead: 'arrow',
endArrowhead: null,
});
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test asserts startArrowhead: 'arrow', endArrowhead: null for a reverse connection while also asserting that source/target are swapped (logical B→A). That combination makes the arrowhead placement inconsistent with the edge direction in Excalidraw (single-headed arrows normally use endArrowhead: 'arrow' on the target side). Consider updating the expected style to default arrowheads (start null, end 'arrow') or otherwise align with whatever convention you choose (either swap endpoints or flip arrowheads, but not both).

Copilot uses AI. Check for mistakes.
Comment on lines +74 to +78
const arrows = file.elements.filter((element) => element.type === 'arrow');

expect(arrows).toHaveLength(2);
expect(arrows[0]).toMatchObject({ startArrowhead: 'arrow', endArrowhead: null });
expect(arrows[1]).toMatchObject({ startArrowhead: 'arrow', endArrowhead: 'arrow' });
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test relies on arrows[0] / arrows[1] ordering after running ELK layout. layoutGraph() builds layoutedEdges by iterating layoutResult.edges, and ELK doesn’t guarantee stable edge ordering, so this can be flaky. Prefer asserting on the multiset of {startArrowhead,endArrowhead} pairs (order-independent) or locating each arrow by its bound element IDs / edge IDs.

Copilot uses AI. Check for mistakes.
Comment on lines +135 to +139
const arrows = file.elements.filter((element: { type: string }) => element.type === 'arrow');

expect(arrows).toHaveLength(2);
expect(arrows[0]).toMatchObject({ startArrowhead: 'arrow', endArrowhead: null });
expect(arrows[1]).toMatchObject({ startArrowhead: 'arrow', endArrowhead: 'arrow' });
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test assumes the first/second arrow element corresponds to a specific edge. Since edge ordering can change during ELK layout (and therefore element emission order), asserting on arrows[0] vs arrows[1] may be flaky. Make the assertion order-independent (e.g., compare sorted arrowhead pairs or find arrows by edge id / bindings). Also ensure the expected arrowhead placement for <- aligns with the chosen semantics (swap endpoints vs flip arrowheads).

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants